package com.tiancheng.trade.payment.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tiancheng.commom.pay.PayOrderAutoQueryConfig;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.enums.*;
import com.tiancheng.trade.commom.core.enums.MessageTypeEnum;
import com.tiancheng.trade.commom.core.exception.Asserts;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.AmountUtils;
import com.tiancheng.trade.commom.core.utils.IdWorkerUtils;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.web.cache.model.MerchantBO;
import com.tiancheng.trade.commom.web.model.request.MerchantChannelRequestDTO;
import com.tiancheng.trade.commom.web.model.request.RefundVerificationRequestVO;
import com.tiancheng.trade.commom.web.model.response.MerchantChannelResponseDTO;
import com.tiancheng.trade.commom.web.model.response.SettlementRuleResVO;
import com.tiancheng.trade.payment.bo.OrgPaymentChannelDetailBO;
import com.tiancheng.trade.payment.bo.meesage.PaymentCommonMessageBO;
import com.tiancheng.trade.payment.channel.AbstractPayChannel;
import com.tiancheng.trade.payment.channel.PayChannelFactory;
import com.tiancheng.trade.payment.channel.bo.*;
import com.tiancheng.trade.payment.channel.enums.ChannelOrderStatusEnum;
import com.tiancheng.trade.payment.enums.*;
import com.tiancheng.trade.payment.mapper.PaymentRefundFlowMapper;
import com.tiancheng.trade.payment.model.*;
import com.tiancheng.trade.payment.mq.PaymentCenterMsgProducer;
import com.tiancheng.trade.payment.service.*;
import com.tiancheng.trade.payment.util.ExcelBuildUtil;
import com.tiancheng.trade.payment.util.ObjectConvertUtil;
import com.tiancheng.trade.payment.util.cache.PaymentCache;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import com.tiancheng.trade.payment.util.lock.Lock;
import com.tiancheng.trade.payment.vo.admin.PaymentRefundFlowQueryVo;
import com.tiancheng.trade.payment.vo.admin.res.PaymentRefundQueryResVO;
import com.tiancheng.trade.payment.vo.api.GetRefundOrderVo;
import com.tiancheng.trade.payment.vo.api.RefundOrderVO;
import com.tiancheng.trade.payment.vo.api.RefundSubOrderVO;
import com.tiancheng.trade.commom.web.model.response.RefundOrderResVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 退款
 */
@Slf4j
@Service
public class PaymentRefundServiceImpl extends ServiceImpl<PaymentRefundFlowMapper, PaymentRefundFlow> implements PaymentRefundService {

    @Resource
    private PaymentOrderService paymentOrderService;
    @Resource
    private PaymentFlowDetailService paymentFlowDetailService;
    @Resource
    private PaymentRefundFlowMapper paymentRefundFlowMapper;
    @Autowired
    private PaymentRefundFlowDetailService refundFlowDetailService;
    @Resource
    @Lazy
    private NotifyService notifyService;
    @Resource
    private OrgPaymentChannelService orgPaymentChannelService;
    @Resource
    private PaymentCenterMsgProducer paymentCenterMsgProducer;
    @Resource
    private SettlePlatformService settlePlatformService;
    @Resource
    private PaymentFlowDetailSettleService flowDetailSettleService;
    @Resource
    private IPaymentFlowDetailSettleDetailService flowDetailSettleDetailService;
    @Resource
    private IRefundSettleService refundSettleService;
    @Resource
    private IRefundSettleDetailService refundSettleDetailService;

    /**
     * 退款
     *
     * @param refundOrderVO
     * @return
     */
    //@Transactional(rollbackFor = Exception.class)
    @Override
    public RefundOrderResVO refund(RefundOrderVO refundOrderVO) throws BusinessException {
        PaymentOrder paymentOrder = paymentOrderService.get(refundOrderVO.getPayFlowId());

        final List<PaymentFlowDetail> paymentFlowDetails = paymentFlowDetailService.queryBySubFlowIds(refundOrderVO.getSubOrders().stream().map(RefundSubOrderVO::getSubPayFlowId).collect(Collectors.toList()));
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertNotNull(paymentOrder);
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertNotEmpty(paymentFlowDetails);
        BusinessErrorInfoEnum.CHANNEL_REFUND_NO_PAY.assertIsTrue(FlowStatusEnum.SUCCESS.getCode().equals(paymentOrder.getPayStatus()));
        // 针对支付流水号加锁
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_REFUND, refundOrderVO.getPayFlowId(), 60, 5)) {
            BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL.assertIsTrue(lock.getLockRes());

            // 根据payment_id判断是否重复
            final QueryWrapper<PaymentRefundFlow> q = new QueryWrapper<>();
            q.eq("payment_id", refundOrderVO.getPaymentId()).eq("pay_flow_id", refundOrderVO.getPayFlowId())
                    .ne("refund_status", FlowStatusEnum.FAIL.getCode());
            final List<PaymentRefundFlow> paymentRefundFlows = paymentRefundFlowMapper.selectList(q);
            BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL_PAYMENT_ID_EXIST.assertIsTrue(CollectionUtils.isEmpty(paymentRefundFlows));
            // 校验退款金额，不超过订单金额
            if (paymentOrder.getPaymentChannel().equals(PaymentChannelEnum.CMB.getCode())) {
                long totalRefundAmount = paymentRefundFlowMapper.sumAmountByPayFlowId(refundOrderVO.getPayFlowId());
                BusinessErrorInfoEnum.CHANNEL_REFUND_AMOUNT_OUT.assertIsTrue(
                        paymentOrder.getAmount() >= (totalRefundAmount + refundOrderVO.getAmount()));
            } else {
                long totalRefundAmount = paymentRefundFlowMapper.sumRefundAmountByPayFlowId(refundOrderVO.getPayFlowId());
                BusinessErrorInfoEnum.CHANNEL_REFUND_AMOUNT_OUT.assertIsTrue(
                        paymentOrder.getOrderAmount() >= (totalRefundAmount + refundOrderVO.getAmount()));
            }
            refundOrderVO.getSubOrders().forEach(unifiedSubOrderVO -> {
                if (null == unifiedSubOrderVO.getRefundPlatformDiscount()) {
                    if (unifiedSubOrderVO.getDiscount() == null) {
                        unifiedSubOrderVO.setRefundPlatformDiscount(0);
                    } else {
                        unifiedSubOrderVO.setRefundPlatformDiscount(unifiedSubOrderVO.getDiscount());
                    }
                }
            });
            final OrgPaymentChannelDetailBO paymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(paymentOrder.getOrgPaymentChannelId());
            String flowId = PaymentIdHelper.getRefund(paymentOrder.getUserId(), paymentOrder.getPaymentChannel(), null, paymentChannelDetailBO.getChannelOrderIdPre());

            PaymentRefundFlow refundFlow = new PaymentRefundFlow();
            refundFlow.setFlowId(flowId);
            refundFlow.setPaymentId(refundOrderVO.getPaymentId());
            refundFlow.setPayFlowId(refundOrderVO.getPayFlowId());
            refundFlow.setUserId(paymentOrder.getUserId());
            refundFlow.setUserTel(paymentOrder.getUserTel());
            final int sumRefundPlatformDiscount = refundOrderVO.getSubOrders().stream().mapToInt(RefundSubOrderVO::getRefundPlatformDiscount).sum();
            // 实际退款金额
            refundFlow.setAmount(refundOrderVO.getAmount() - sumRefundPlatformDiscount);
            refundFlow.setRefundPlatformDiscount(sumRefundPlatformDiscount);
            refundFlow.setRefundChannelDiscount(0);
            // 申请退款金额
            refundFlow.setRefundAmount(refundOrderVO.getAmount());
            refundFlow.setOrderAmount(refundOrderVO.getOrderAmount());
            refundFlow.setRefundStatus(FlowStatusEnum.WAITING.getCode());
            refundFlow.setOrderName(paymentOrder.getOrderName());
            refundFlow.setOrderApp(paymentOrder.getOrderApp());
            refundFlow.setPaymentChannel(paymentOrder.getPaymentChannel());
            refundFlow.setPaymentWay(paymentOrder.getPaymentWay());
            refundFlow.setCreateTime(LocalDateTime.now());
            refundFlow.setPayScene(paymentOrder.getPayScene());
            refundFlow.setSlottingRate(paymentOrder.getSlottingRate());
            refundFlow.setOrgPaymentChannelId(paymentOrder.getOrgPaymentChannelId());

            paymentRefundFlowMapper.insert(refundFlow);
            AtomicInteger subFlowInc = new AtomicInteger(1); // 子订单流水起始值
            List<PaymentRefundFlowDetail> refundFlowDetails = refundOrderVO.getSubOrders().stream().map(d -> {
                PaymentFlowDetail first = paymentFlowDetails.stream().filter(pd -> pd.getSubFlowId().equals(d.getSubPayFlowId())).findFirst().get();
                PaymentRefundFlowDetail detail = ObjectConvertUtil.convert(d, PaymentRefundFlowDetail.class).get();
                detail.setFlowId(flowId);
                detail.setPaymentId(refundOrderVO.getPaymentId());
                detail.setSubPaymentId(d.getSubPaymentId());
                detail.setRefundStatus(FlowStatusEnum.WAITING.getCode());
                detail.setSubFlowId(PaymentIdHelper.getSubOrderFlowId(flowId, subFlowInc.get()));
                detail.setRefundPlatformDiscount(d.getRefundPlatformDiscount());
                detail.setRefundAmount(d.getAmount());
                detail.setCouponSettleStatus("");
                detail.setAmount(detail.getRefundAmount() - detail.getRefundPlatformDiscount());
                detail.setSubPayFlowId(first.getSubFlowId());
                subFlowInc.getAndIncrement();
                return detail;
            }).collect(Collectors.toList());
            refundFlowDetailService.saveBatch(refundFlowDetails);

            // 渠道退款
            RefundOrderBO refundOrderBO = new RefundOrderBO();
            refundOrderBO.setUid(paymentOrder.getUserId());
            refundOrderBO.setRefundId(flowId);
            refundOrderBO.setOrderId(paymentOrder.getFlowId());
            refundOrderBO.setOrderPayTime(paymentOrder.getFlowTime());
            refundOrderBO.setTransactionId(paymentOrder.getTransactionId());
            refundOrderBO.setAmount(refundOrderVO.getAmount());
            refundOrderBO.setOrderAmount(refundOrderVO.getOrderAmount());
            refundOrderBO.setDiscount(refundOrderVO.getDiscount());
            refundOrderBO.setRefundPlatformDiscount(sumRefundPlatformDiscount);
            refundOrderBO.setSubOrders(new ArrayList<>());

            refundOrderVO.getSubOrders().forEach(refundSubVO -> {
                RefundOrderBO.SubOrderBO subOrderBO = new RefundOrderBO.SubOrderBO();
                subOrderBO.setOrderAmount(refundSubVO.getOrderAmount());
                subOrderBO.setAmount(refundSubVO.getAmount());
                subOrderBO.setSubOrderId(refundSubVO.getSubOrderId());
                subOrderBO.setDiscount(refundSubVO.getDiscount());
                subOrderBO.setRefundPlatformDiscount(refundSubVO.getRefundPlatformDiscount());
                List<PaymentRefundFlowDetail> collect = refundFlowDetails.stream().filter(r -> r.getSubOrderId().equals(refundSubVO.getSubOrderId())).collect(Collectors.toList());
                subOrderBO.setSubRefundId(collect.get(0).getSubFlowId());// 部分退款时使用
                refundOrderBO.getSubOrders().add(subOrderBO);
            });

            AbstractPayChannel payChannel = PayChannelFactory.get(paymentOrder.getPaymentChannel());
            refundOrderBO.setReason(refundOrderVO.getReason());
            refundOrderBO.setOrgPaymentChannelId(paymentOrder.getOrgPaymentChannelId());
            refundOrderBO.setBusinessCode(paymentOrder.getBusinessCode());
            refundOrderBO.setPaymentChannelCode(paymentOrder.getPaymentChannel());

            // 生成退款结算明细
            List<RefundOrderSettleBO> refundOrderSettleBOS = genSettle(paymentOrder, paymentFlowDetails, refundFlowDetails, paymentChannelDetailBO);
            refundOrderBO.setSettles(refundOrderSettleBOS);

            Result<RefundOrderResBO> result = payChannel.refund(refundOrderBO);
            log.info("{}退款结果:{}", payChannel.getPaymentChannelEnum().getDesc(), JacksonUtils.toJson(result));
            // 同步返回退款失败
            if (result.getRet() != 0) {
                QueryWrapper<PaymentRefundFlow> conditions = new QueryWrapper<>();
                conditions.eq("user_id", paymentOrder.getUserId());
                conditions.eq("flow_id", flowId);

                PaymentRefundFlow updateRefundFlow = new PaymentRefundFlow();
                updateRefundFlow.setRefundStatus(FlowStatusEnum.FAIL.getCode());
                updateRefundFlow.setFlowTime(LocalDateTime.now());
                if (ChannelRefundTypeEnum.SPLIT.getCode().equals(this.getRefundType(result))) {
                    updateRefundFlow.setRefundType(ChannelRefundTypeEnum.SPLIT.getCode());
                }
                paymentRefundFlowMapper.update(updateRefundFlow, conditions);
                refundFlowDetailService.updateRefundStatus(flowId, null, ChannelRefundTypeEnum.ALL,
                        FlowStatusEnum.FAIL);
                //  抛出异常，返回订单中心退款失败
                BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL.assertFail();
            } else {
                BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL.assertIsTrue(result.getRet() == 0);
                boolean needUpdateRefundFlow = false;
                PaymentRefundFlow updateRefundFlow = new PaymentRefundFlow();
                if (result.getData() != null && result.getData().getRefundInvoiceAmount() != null && result.getData().getRefundInvoiceAmount() > 0) {
                    // 设置实付部分退款金额
                    needUpdateRefundFlow = true;
                    updateRefundFlow.setAmount(result.getData().getRefundInvoiceAmount());
                    if (ChannelRefundTypeEnum.SPLIT.getCode().equals(this.getRefundType(result))) {
                        updateRefundFlow.setRefundType(ChannelRefundTypeEnum.SPLIT.getCode());
                    }
                    refundFlow.setAmount(result.getData().getRefundInvoiceAmount());
                }

                if (needUpdateRefundFlow) {
                    QueryWrapper<PaymentRefundFlow> conditions = new QueryWrapper<>();
                    conditions.eq("user_id", paymentOrder.getUserId());
                    conditions.eq("flow_id", flowId);
                    paymentRefundFlowMapper.update(updateRefundFlow, conditions);
                }
            }
            // 自动查询退款状态，发送到消息队列
            sendQueryRefundMsg2Mq(paymentOrder, flowId, payChannel);

            RefundOrderResVO refundOrderResVO = new RefundOrderResVO();
            refundOrderResVO.setFlowId(refundFlow.getFlowId());
            refundOrderResVO.setPaymentId(refundFlow.getPaymentId());
            refundOrderResVO.setSlottingRate(refundFlow.getSlottingRate());
            refundOrderResVO.setRefundAmount(refundOrderVO.getAmount());
            return refundOrderResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL, null, e.getMessage(), e);
        }
    }

    /**
     * 生成退款结算信息
     *
     * @param paymentOrder       支付流水
     * @param paymentFlowDetails 支付流水明细
     * @param refundFlowDetails  退款明细
     */
    private List<RefundOrderSettleBO> genSettle(PaymentOrder paymentOrder, List<PaymentFlowDetail> paymentFlowDetails,
                                                List<PaymentRefundFlowDetail> refundFlowDetails, OrgPaymentChannelDetailBO paymentChannelDetailBO) {

        // 查询分账商户信息
        List<MerchantChannelRequestDTO> merchants = new ArrayList<>();
        for (PaymentFlowDetail paymentFlowDetail : paymentFlowDetails) {
            MerchantChannelRequestDTO mcrd = new MerchantChannelRequestDTO();
            mcrd.setMerchantCode(paymentFlowDetail.getMerchantCode());
            mcrd.setPaymentChannelCode(paymentOrder.getPaymentChannel());
            mcrd.setOrgPaymentChannelId(paymentOrder.getOrgPaymentChannelId());
            mcrd.setBusinessCode(paymentOrder.getBusinessCode());
            merchants.add(mcrd);
        }
        Map<String, MerchantBO> merchantInfo = settlePlatformService.getMerchantInfo(merchants);
        Asserts.isTrue(!merchantInfo.isEmpty(), "商户分账信息配置异常！");
        // 查询商户清分规则
        Map<String, SettlementRuleResVO> settlementRuleResVOMap = settlePlatformService.querySettleRules(paymentFlowDetails.stream().map(PaymentFlowDetail::getSettleRuleCode).collect(Collectors.toSet()));
        Asserts.isTrue(null != settlementRuleResVOMap && settlementRuleResVOMap.size() > 0, "商户清分信息配置异常！");

        // 查询支付订单结算信息
        List<PaymentFlowDetailSettle> paymentFlowDetailSettles = flowDetailSettleService.queryBySubFlowIds(paymentOrder.getFlowId(), paymentFlowDetails.stream().map(PaymentFlowDetail::getSubFlowId).collect(Collectors.toSet()));
        // 查询支付订单结算明细信息
        Map<String, List<PaymentFlowDetailSettleDetail>> flowDetailSettleDetails = flowDetailSettleDetailService.listBySettleIds(paymentFlowDetailSettles.stream().map(PaymentFlowDetailSettle::getId).collect(Collectors.toSet())).stream()
                .collect(Collectors.groupingBy(PaymentFlowDetailSettleDetail::getSubFlowId));
        // 查询机构收款子商户
        Map<Long, MerchantChannelResponseDTO> orgPaymentMerchant = queryOrgPaymentMerchant(paymentOrder.getOrgPaymentChannelId());
        List<RefundOrderSettleBO> settles = new ArrayList<RefundOrderSettleBO>();

        // 退款结算
        List<RefundSettle> refundSettles = new ArrayList<>();
        // 退款结算明细
        List<RefundSettleDetail> refundSettleDetails = new ArrayList<>();
        // 退款流水
        for (PaymentRefundFlowDetail refundFlowDetail : refundFlowDetails) {
            PaymentFlowDetailSettle paymentFlowDetailSettle = paymentFlowDetailSettles.stream().filter(p -> p.getSubFlowId().equals(refundFlowDetail.getSubPayFlowId())).findFirst().get();
            RefundOrderSettleBO refundOrderSettleBO = new RefundOrderSettleBO();
            refundOrderSettleBO.setFlowId(refundFlowDetail.getFlowId());
            refundOrderSettleBO.setSubFlowId(refundFlowDetail.getSubFlowId());
            refundOrderSettleBO.setPayFlowId(paymentOrder.getFlowId());
            refundOrderSettleBO.setSubPayFlowId(refundFlowDetail.getSubPayFlowId());

            // 先查找支付子流水号
            Optional<PaymentFlowDetail> first = paymentFlowDetails.stream().filter(p -> p.getSubFlowId().equals(refundFlowDetail.getSubPayFlowId())).findFirst();
            Asserts.isTrue(first.isPresent(), "商户分账信息配置异常，未查询到支付明细！");
            MerchantBO merchantBO = merchantInfo.get(first.get().getMerchantCode());
            Asserts.isNotNull(merchantBO, "商户分账信息配置异常，未查询到分账商户，商户号:" + first.get().getMerchantCode());
            refundOrderSettleBO.setMerchantRegId(merchantBO.getMerchantRegId());
            refundOrderSettleBO.setMerchantCode(first.get().getMerchantCode());

            SettlementRuleResVO merchantRule = settlementRuleResVOMap.get(first.get().getSettleRuleCode());
            Asserts.isTrue(null != merchantRule, "商户清分信息配置异常,未查询到清分规则！");
            // 根据退款金额查询返佣金额
            final Integer subOrderPlatformAmount = settlePlatformService.platformAmountCommissionMerchantSettleRule(refundFlowDetail.getRefundAmount(), merchantRule);
            refundOrderSettleBO.setPlatformAmount(subOrderPlatformAmount);
            refundOrderSettleBO.setRefundAmount(refundFlowDetail.getRefundAmount());

            // 生成退款结算流水
            RefundSettle refundSettle = new RefundSettle();
            refundSettle.setId(IdWorkerUtils.getInstance().nextId());
            refundSettle.setFlowId(refundFlowDetail.getFlowId());
            refundSettle.setSubFlowId(refundFlowDetail.getSubFlowId());
            refundSettle.setSubPaymentId(refundFlowDetail.getSubPaymentId());
            refundSettle.setPayFlowId(paymentOrder.getFlowId());
            refundSettle.setSubPayFlowId(first.get().getSubFlowId());
            refundSettle.setTradeTime(LocalDateTime.now());
            refundSettle.setRefundAmount(refundFlowDetail.getRefundAmount());
            refundSettle.setPlatformAmount(AmountUtils.negativeAmount(subOrderPlatformAmount));
            refundSettle.setMerchantCode(first.get().getMerchantCode());
            refundSettle.setMerchantName(first.get().getMerchantName());
            refundSettle.setSubMerchantId(merchantBO.getSubAppid());
            refundSettle.setSettleRuleType(paymentFlowDetailSettle.getSettleRuleType());
            refundSettle.setSettleRuleCode(paymentFlowDetailSettle.getSettleRuleCode());
            // 支付回调后触发核销
            refundSettle.setVerificationStatus(VerificationStatusEnum.WAIT.getCode());
            refundSettle.setSettleStatus(SettleStatusEnum.WAIT.getCode());
            refundSettle.setOrgPaymentChannelId(paymentOrder.getOrgPaymentChannelId());
            refundSettle.setPaymentChannel(paymentOrder.getPaymentChannel());
            refundSettle.setCreateTime(LocalDateTime.now());
            String settleFlowId = PaymentIdHelper.getSettleFlowId(refundFlowDetail.getSubFlowId(), paymentChannelDetailBO.getChannelOrderIdPre());
            refundSettle.setSettleFlowId(settleFlowId);


            int subFlowInc = 1; // 子订单流水起始值
            // 分账明细项:平台佣金
            MerchantChannelResponseDTO opm = orgPaymentMerchant.get(paymentOrder.getOrgPaymentChannelId());
            // 子订单对应的分账明细
            List<PaymentFlowDetailSettleDetail> subFlowSetlles = flowDetailSettleDetails.get(refundFlowDetail.getSubPayFlowId());
            // 子订单的总金额（所有分账方的总和）
            Integer sumAmount = subFlowSetlles.stream().mapToInt(PaymentFlowDetailSettleDetail::getAmount).sum();
            List<RefundOrderSettleBO.RefundOrderSettleItemBO> settleItemBOS = new ArrayList<>();
            // 遍历所有分账方，生成分账方退佣金额信息

            // 商户分账金额
            int merchantAmount = 0;
            for (int i = 0; i < subFlowSetlles.size(); i++) {
                PaymentFlowDetailSettleDetail pfdsd = subFlowSetlles.get(i);
                // 商户id
                RefundOrderSettleBO.RefundOrderSettleItemBO item = new RefundOrderSettleBO.RefundOrderSettleItemBO();
                item.setMerOrderId(PaymentIdHelper.getSubOrderFlowId(settleFlowId, subFlowInc));
                item.setOriMerOrderId(pfdsd.getSubSettleFlowId());
                item.setIsPlatformMerchant(pfdsd.getMerchantRegId().equals(opm.getMerchantRegId()));
                item.setMerchantRegId(pfdsd.getMerchantRegId());
                item.setMerchantCode(pfdsd.getMerchantCode());
                item.setEnterpriseCode(pfdsd.getSubMerchantId());
                item.setMid(pfdsd.getSubMerchantId());
                if (i == subFlowSetlles.size() - 1) {
                    // 最后一单做减法
                    int amount = refundFlowDetail.getRefundAmount() - settleItemBOS.stream().mapToInt(RefundOrderSettleBO.RefundOrderSettleItemBO::getTotalAmount).sum();
                    if (amount == 0) {
                        break;
                    }
                    item.setTotalAmount(amount);
                } else {
                    // 计算应当返佣的金额
                    int divide = BigDecimal.valueOf(pfdsd.getAmount()).divide(BigDecimal.valueOf(sumAmount), 10, RoundingMode.DOWN).multiply(BigDecimal.valueOf(refundFlowDetail.getRefundAmount())).setScale(0, RoundingMode.HALF_UP).intValue();
                    item.setTotalAmount(divide);
                }
                subFlowInc++;
                settleItemBOS.add(item);

                // 生成退款结算明细、
                RefundSettleDetail refundSettleDetail = new RefundSettleDetail();
                refundSettleDetail.setSettleId(refundSettle.getId());
                refundSettleDetail.setSubFlowId(refundSettle.getSubFlowId());
                refundSettleDetail.setSubSettleFlowId(item.getMerOrderId());
                refundSettleDetail.setSubMerchantType(pfdsd.getSubMerchantType());
                refundSettleDetail.setMerchantCode(item.getMerchantCode());
                refundSettleDetail.setMerchantName(pfdsd.getMerchantName());
                refundSettleDetail.setMerchantRegId(pfdsd.getMerchantRegId());
                refundSettleDetail.setSubMerchantId(item.getMid());
                refundSettleDetail.setAmount(AmountUtils.negativeAmount(item.getTotalAmount()));
                refundSettleDetail.setSettleMerchantId(item.getEnterpriseCode());
                refundSettleDetail.setWithdrawalId("");
                refundSettleDetail.setSettleStatus(SettleStatusEnum.WAIT.getCode());
                if (refundSettleDetail.getSubMerchantType().equals(1)) {
                    merchantAmount += refundSettleDetail.getAmount();
                }

                refundSettleDetails.add(refundSettleDetail);
            }
            refundSettle.setMerchantAmount(merchantAmount);
            refundSettles.add(refundSettle);
            refundOrderSettleBO.setSettleItems(settleItemBOS);
            settles.add(refundOrderSettleBO);
        }

        refundSettleService.saveBatch(refundSettles);
        refundSettleDetailService.saveBatch(refundSettleDetails);
        return settles;
    }

    /**
     * 查询 清分平台分账子商户
     *
     * @param orgPaymentChannelId 机构支付通道id
     * @return
     */
    private Map<Long, MerchantChannelResponseDTO> queryOrgPaymentMerchant(Long orgPaymentChannelId) {
        List<MerchantChannelResponseDTO> orgSubMerchant = settlePlatformService.queryOrgSubMerchant(Collections.singletonList(orgPaymentChannelId));
        Asserts.isTrue(org.apache.commons.collections4.CollectionUtils.isNotEmpty(orgSubMerchant), "未设置平台分账收款子商户！");
        HashMap<Long, MerchantChannelResponseDTO> result = new HashMap<>();
        result.put(orgPaymentChannelId, orgSubMerchant.get(0));
        return result;
    }

    private void sendQueryRefundMsg2Mq(PaymentOrder paymentOrder, String flowId, AbstractPayChannel payChannel) {
        // 判断是否需要主动查询订单状态
        if (payChannel.autoQueryStatus(paymentOrder.getOrgPaymentChannelId())) {
            // 将查询参数放入队列
            PayOrderAutoQueryConfig orderAutoQueryConfig = payChannel.getOrderAutoQueryConfig(paymentOrder.getOrgPaymentChannelId());
            if (Objects.nonNull(orderAutoQueryConfig) && orderAutoQueryConfig.getEnable() && orderAutoQueryConfig.getRetryCount() != 0) {
                if (orderAutoQueryConfig.getRetryInterval() <= 3000) {
                    orderAutoQueryConfig.setRetryInterval(3000);
                }
                if (orderAutoQueryConfig.getRetryCount() >= 100) {
                    orderAutoQueryConfig.setRetryCount(100);
                }
                PaymentCommonMessageBO messageBO = new PaymentCommonMessageBO();
                messageBO.setMessageType(MessageTypeEnum.trade_payment_common_query_refund_result);
                messageBO.setRetryCount(orderAutoQueryConfig.getRetryCount());
                messageBO.setRetryInterval(orderAutoQueryConfig.getRetryInterval());
                messageBO.setCurrentRetryPosition(1);

                GetRefundOrderVo getRefundOrderVo = new GetRefundOrderVo();
                getRefundOrderVo.setFlowId(flowId);
                messageBO.setMessageBody(JsonUtil.toJsonString(getRefundOrderVo));
                paymentCenterMsgProducer.setPaymentQueryMsg(messageBO);
                PaymentCache.cachePaymentStatusQueryParams(flowId);
                log.info("主动查询订单【退款状态】，存入延迟消息队列：{}", JsonUtil.toJsonString(messageBO));
            }
        }
    }

    @Override
    public RefundOrderResVO deductionRefund(RefundOrderVO refundOrderVO) throws BusinessException {
        PaymentOrder paymentOrder = paymentOrderService.get(refundOrderVO.getPayFlowId());
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertNotNull(paymentOrder);
        BusinessErrorInfoEnum.CHANNEL_REFUND_NO_PAY.assertIsTrue(FlowStatusEnum.SUCCESS.getCode().equals(paymentOrder.getPayStatus()));
        BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL_NOT_SUPPORT_DEDUCTION_REFUND.assertIsTrue(paymentOrder.getPaymentWay().equalsIgnoreCase(PaymentWayEnum.DEDUCTION.getCode()));

        // 针对支付流水号加锁
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_REFUND, refundOrderVO.getPayFlowId(), 60, 5)) {
            BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL.assertIsTrue(lock.getLockRes());
            // 校验退款金额，不超过订单金额
            long totalRefundAmount = paymentRefundFlowMapper.sumRefundAmountByPayFlowId(refundOrderVO.getPayFlowId());
            BusinessErrorInfoEnum.CHANNEL_REFUND_AMOUNT_OUT.assertIsTrue(
                    paymentOrder.getOrderAmount() >= (totalRefundAmount + refundOrderVO.getAmount()));
            refundOrderVO.getSubOrders().forEach(unifiedSubOrderVO -> {
                if (null == unifiedSubOrderVO.getRefundPlatformDiscount()) {
                    if (unifiedSubOrderVO.getDiscount() == null) {
                        unifiedSubOrderVO.setRefundPlatformDiscount(0);
                    } else {
                        unifiedSubOrderVO.setRefundPlatformDiscount(unifiedSubOrderVO.getDiscount());
                    }
                }
            });
            final OrgPaymentChannelDetailBO paymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(paymentOrder.getOrgPaymentChannelId());
            String flowId = PaymentIdHelper.getRefund(paymentOrder.getUserId(), paymentOrder.getPaymentChannel(), "notPay", paymentChannelDetailBO.getChannelOrderIdPre());
            final int sumRefundPlatformDiscount = refundOrderVO.getSubOrders().stream().mapToInt(RefundSubOrderVO::getRefundPlatformDiscount).sum();

            PaymentRefundFlow refundFlow = new PaymentRefundFlow();
            refundFlow.setFlowId(flowId);
            refundFlow.setPaymentId(refundOrderVO.getPaymentId());
            refundFlow.setPayFlowId(refundOrderVO.getPayFlowId());
            refundFlow.setUserId(paymentOrder.getUserId());
            refundFlow.setUserTel(paymentOrder.getUserTel());
            // 实际退款金额为0
            refundFlow.setAmount(0);
            refundFlow.setRefundPlatformDiscount(sumRefundPlatformDiscount);
            refundFlow.setRefundChannelDiscount(0);
            // 申请退款金额
            refundFlow.setRefundAmount(refundOrderVO.getAmount());
            refundFlow.setOrderAmount(refundOrderVO.getOrderAmount());
            refundFlow.setRefundStatus(FlowStatusEnum.WAITING.getCode());
            refundFlow.setOrderName(paymentOrder.getOrderName());
            refundFlow.setOrderApp(paymentOrder.getOrderApp());
            refundFlow.setPaymentChannel(paymentOrder.getPaymentChannel());
            refundFlow.setPaymentWay(paymentOrder.getPaymentWay());
            refundFlow.setCreateTime(LocalDateTime.now());
            refundFlow.setPayScene(paymentOrder.getPayScene());
            refundFlow.setSlottingRate(paymentOrder.getSlottingRate());

            paymentRefundFlowMapper.insert(refundFlow);
            AtomicInteger subFlowInc = new AtomicInteger(1); // 子订单流水起始值
            List<PaymentRefundFlowDetail> refundFlowDetails = refundOrderVO.getSubOrders().stream().map(d -> {
                PaymentRefundFlowDetail detail = ObjectConvertUtil.convert(d, PaymentRefundFlowDetail.class).get();
                detail.setFlowId(flowId);
                detail.setPaymentId(refundOrderVO.getPaymentId());
                detail.setRefundStatus(FlowStatusEnum.WAITING.getCode());
                detail.setSubFlowId(PaymentIdHelper.getSubOrderFlowId(flowId, subFlowInc.get()));
                detail.setRefundPlatformDiscount(d.getRefundPlatformDiscount());
                detail.setDiscount(d.getAmount());
                detail.setRefundAmount(d.getAmount());
                detail.setCouponSettleStatus("");
                detail.setAmount(0);
                subFlowInc.getAndIncrement();
                return detail;
            }).collect(Collectors.toList());
            refundFlowDetailService.saveBatch(refundFlowDetails);

            RefundOrderResVO refundOrderResVO = new RefundOrderResVO();
            refundOrderResVO.setFlowId(refundFlow.getFlowId());
            refundOrderResVO.setPaymentId(refundFlow.getPaymentId());
            refundOrderResVO.setSlottingRate(refundFlow.getSlottingRate());
            refundOrderResVO.setRefundAmount(refundOrderVO.getAmount());

            final RefundResultBO bo = new RefundResultBO();
            bo.setRefundId(flowId);
            bo.setAmount(0);
            bo.setRefundAmount(refundFlow.getRefundAmount());
            bo.setRefundTime(LocalDateTime.now());
            bo.setStatus(FlowStatusEnum.SUCCESS.getCode());
            bo.setPaymentChannel(PaymentChannelEnum.UMS);
            bo.setPayFlowId(refundOrderVO.getPayFlowId());
            notifyService.mockRefundNotify(bo);
            return refundOrderResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL, null, e.getMessage(), e);
        }
    }

    /**
     * 退款类型
     *
     * @param result
     * @return
     */
    private int getRefundType(Result<RefundOrderResBO> result) {
        if (result.getData() != null && result.getData().getRefundTypeEnum() != null) {
            return result.getData().getRefundTypeEnum().getCode();
        }
        return ChannelRefundTypeEnum.ALL.getCode();
    }

    @Override
    public PaymentRefundFlow findByFlowId(String flowId) {
        QueryWrapper<PaymentRefundFlow> wrapper = new QueryWrapper<>();
        wrapper.eq("flow_id", flowId);
        return paymentRefundFlowMapper.selectOne(wrapper);
    }

    @Override
    public PaymentRefundFlow get(Long id) {
        return paymentRefundFlowMapper.selectById(id);
    }

    @Override
    public PaymentRefundQueryResVO getInfo(Long id) {
        PaymentRefundFlow paymentRefundFlow = paymentRefundFlowMapper.selectById(id);
        PaymentRefundQueryResVO vo = ObjectConvertUtil.convert(paymentRefundFlow, PaymentRefundQueryResVO.class).get();
        vo.setPaymentChannelCn(PaymentChannelEnum.getDesc(vo.getPaymentChannel()));
        vo.setPaymentWayCn(PaymentWayEnum.getDesc(vo.getPaymentWay()));
        vo.setPaySceneCn(PaySceneEnum.getDesc(vo.getPayScene()));
        vo.setRefundStatusCn(FlowStatusEnum.getDesc(vo.getRefundStatus()));
        return vo;
    }

    @Override
    public long add(PaymentRefundFlow PaymentRefundFlow) {
        int num = paymentRefundFlowMapper.insert(PaymentRefundFlow);
        BusinessErrorInfoEnum.PAYMENT_ORDER_ADD_ERROR.assertIsTrue(num > 0);
        return PaymentRefundFlow.getId();
    }

    @Override
    public PaymentRefundFlow findByPaymentId(String paymentId) {
        QueryWrapper<PaymentRefundFlow> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("payment_id", paymentId);
        PaymentRefundFlow paymentRefundFlow = paymentRefundFlowMapper.selectOne(queryWrapper);
        return paymentRefundFlow;
    }

    @Override
    public PageInfo findList(PaymentRefundFlowQueryVo vo) {
        int page = vo.getCurrentPage();
        int limit = vo.getPageSize();

        QueryWrapper<PaymentRefundFlow> query = buildQuery(vo);
        query.orderByDesc("create_time");
        PageInfo<PaymentRefundFlow> pageInfo = paymentRefundFlowMapper.find(query, page, limit);
        PageInfo<PaymentRefundQueryResVO> pageRes = new PageInfo<>();
        List<PaymentRefundQueryResVO> vos = pageInfo.getList().stream().map(refund ->
                ObjectConvertUtil.convert(refund, PaymentRefundQueryResVO.class).get()).collect(Collectors.toList());
        vos.forEach(obj -> {
            obj.setPaymentChannelCn(PaymentChannelEnum.getDesc(obj.getPaymentChannel()));
            obj.setPaymentWayCn(PaymentWayEnum.getDesc(obj.getPaymentWay()));
            obj.setPaySceneCn(PaySceneEnum.getDesc(obj.getPayScene()));
            obj.setRefundStatusCn(FlowStatusEnum.getDesc(obj.getRefundStatus()));
        });
        pageRes.setList(vos);
        pageRes.setPagination(pageInfo.getPagination());
        return pageRes;
    }

    private QueryWrapper<PaymentRefundFlow> buildQuery(PaymentRefundFlowQueryVo vo) {
        QueryWrapper<PaymentRefundFlow> query = new QueryWrapper<>();

        if (StringUtils.isNotBlank(vo.getFlowId())) {
            query.eq("flow_id", vo.getFlowId());
        }
        if (StringUtils.isNotBlank(vo.getPayFlowId())) {
            query.eq("pay_flow_id", vo.getPayFlowId());
        }

        if (StringUtils.isNotBlank(vo.getPaymentId())) {
            query.eq("payment_id", vo.getPaymentId());
        }

        if (StringUtils.isNotBlank(vo.getRefundStatus())) {
            query.eq("refund_status", vo.getRefundStatus());
        }
        if (StringUtils.isNotBlank(vo.getPaymentChannel())) {
            query.eq("payment_channel", vo.getPaymentChannel());
        }

        if (vo.getFlowTimeBegin() != null) {
            query.ge("flow_time", vo.getFlowTimeBegin());
        }
        if (vo.getFlowTimeEnd() != null) {
            query.lt("flow_time", vo.getFlowTimeEnd().plusDays(1));
        }

        if (vo.getCreateTimeBegin() != null) {
            query.ge("create_time", vo.getCreateTimeBegin());
        }
        if (vo.getCreateTimeEnd() != null) {
            query.lt("create_time", vo.getCreateTimeEnd().plusDays(1));
        }
        return query;
    }

    @Override
    public PaymentRefundFlow refundResult(RefundResultBO refundResultBO) throws BusinessException {
        // 退款流水号处理，部分退款使用了退款自流水单号，截断为锁整笔退款流水
        String refundId = refundResultBO.getRefundId();
        if (refundId.length() > 24) {
            // 银联的主退款单号是24位
            refundId = refundId.substring(0, 24);
        }
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_CALLBACK_REFUND, refundId, 0, 5)) {
            // 针对流水号加锁，如果并发重复通知，未获取锁的抛出重复异常
            BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<PaymentRefundFlow> conditions = new QueryWrapper<>();
            conditions.eq("flow_id", refundResultBO.getRefundId());

            PaymentRefundFlow refundFlow = paymentRefundFlowMapper.selectOne(conditions);
            if (null == refundFlow && refundResultBO.getPaymentChannel().getCode().equals(PaymentChannelEnum.UMS.getCode())) {
                // 处理拆单退款的情况
                PaymentRefundFlowDetail rd = refundFlowDetailService.findBySubFlowId(refundResultBO.getRefundId());
                BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_NO_FLOW.assertNotNull(rd);
                BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_SUCCESS_DUP.assertIsTrue(!FlowStatusEnum.SUCCESS.getCode().equals(rd.getRefundStatus()));
                refundFlowDetailService.updateRefundStatus(rd.getFlowId(), rd.getSubOrderId(), ChannelRefundTypeEnum.SPLIT,
                        "success".equals(refundResultBO.getStatus()) ? FlowStatusEnum.SUCCESS : FlowStatusEnum.FAIL);

                List<PaymentRefundFlowDetail> flowDetails = refundFlowDetailService.findByFlowId(rd.getFlowId());
                List<PaymentRefundFlowDetail> successOrders = flowDetails.stream().filter(d -> d.getRefundStatus().equals(FlowStatusEnum.SUCCESS.getCode())).collect(Collectors.toList());
                if (successOrders.size() != flowDetails.size()) {
                    // 回调还没完全回来
                    log.info("拆单退款回调，第{}单，订单号：{},退款流水：{}", successOrders.size(), rd.getSubOrderId(), rd.getSubFlowId());
                    refundFlow = new PaymentRefundFlow();
                    refundFlow.setRefundStatus(FlowStatusEnum.PART_SUCCESS.getCode());
                    return refundFlow;
                }
                log.info("拆单退款回调，第{}单，订单号：{},退款流水：{}", successOrders.size(), rd.getSubOrderId(), rd.getSubFlowId());
                // 所有回调都已经回来
                PaymentRefundFlow updateRefundFlow = new PaymentRefundFlow();
                updateRefundFlow.setTransactionId(refundResultBO.getTransactionId());
                if (StringUtils.isNotEmpty(refundResultBO.getTradeNo())) {
                    updateRefundFlow.setTradeNo(refundResultBO.getTradeNo());
                }
                updateRefundFlow.setFlowTime(refundResultBO.getRefundTime());
                updateRefundFlow.setRefundStatus(FlowStatusEnum.SUCCESS.getCode());
                QueryWrapper<PaymentRefundFlow> updateConditions = new QueryWrapper<>();
                updateConditions.eq("flow_id", rd.getFlowId());
                paymentRefundFlowMapper.update(updateRefundFlow, updateConditions);
                refundFlow = this.getByFlowId(rd.getFlowId());
                refundFlow.setTransactionId(refundResultBO.getTransactionId());
                refundFlow.setTradeNo(refundResultBO.getTradeNo());
                refundFlow.setRefundStatus(updateRefundFlow.getRefundStatus());
                refundFlow.setFlowTime(updateRefundFlow.getFlowTime());

                log.info("拆单退款整单退款成功回调，退款流水：{}", rd.getFlowId());
                return refundFlow;
            } else {
                BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_NO_FLOW.assertNotNull(refundFlow);
                BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_SUCCESS_DUP.assertIsTrue(!FlowStatusEnum.SUCCESS.getCode().equals(refundFlow.getRefundStatus()));

                PaymentRefundFlow updateRefundFlow = new PaymentRefundFlow();
                updateRefundFlow.setTransactionId(refundResultBO.getTransactionId());
                if (StringUtils.isNotEmpty(refundResultBO.getTradeNo())) {
                    updateRefundFlow.setTradeNo(refundResultBO.getTradeNo());
                }
                updateRefundFlow.setFlowTime(refundResultBO.getRefundTime());
                updateRefundFlow.setRefundStatus("success".equals(refundResultBO.getStatus()) ? FlowStatusEnum.SUCCESS.getCode() : FlowStatusEnum.FAIL.getCode());

                paymentRefundFlowMapper.update(updateRefundFlow, conditions);

                // 修改退款明细退款状态
                refundFlowDetailService.updateRefundStatus(refundResultBO.getRefundId(), null, ChannelRefundTypeEnum.ALL,
                        "success".equals(refundResultBO.getStatus()) ? FlowStatusEnum.SUCCESS : FlowStatusEnum.FAIL);

                refundFlow.setTransactionId(refundResultBO.getTransactionId());
                refundFlow.setTradeNo(refundResultBO.getTradeNo());
                refundFlow.setRefundStatus(updateRefundFlow.getRefundStatus());
                refundFlow.setFlowTime(updateRefundFlow.getFlowTime());
            }
            return refundFlow;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_ERROR, null, e.getMessage(), e);
        }
    }


    /**
     * 根据商户查询应清分退款流水
     *
     * @param merchants {@link Collection<String>}
     * @return {@link List<RefundSettleBO>}
     */
    @Override
    public List<RefundSettleBO> listSettleRefund(Collection<String> merchants) {
        return paymentRefundFlowMapper.listSettleRefund(merchants);
    }

    /**
     * 查询清分支付流水对应的当日退款成功退款流水
     *
     * @param flowDetailIds
     * @return
     */
    @Override
    public List<RefundSettleBO> listApprRefund(List<Long> flowDetailIds) {
        return paymentRefundFlowMapper.listApprRefund(flowDetailIds);
    }

    /**
     * 退款流水明细批量更新-所更新字段值相同
     *
     * @param ids       id
     * @param colValMap 字段-值：(column, value)
     * @return
     */
    @Override
    public Integer updateSettleBatch(List<Long> ids, HashMap<String, Object> colValMap) {
        return paymentRefundFlowMapper.updateSettleBatch(ids, colValMap);
    }

    // 导出excel
    @Override
    public Result exportList(PaymentRefundFlowQueryVo vo, HttpServletResponse response) throws IOException {
        QueryWrapper<PaymentRefundFlow> query = buildQuery(vo);
        query.orderByAsc("create_time");
        // 时间范围为空时，默认查询当前月份
        if (vo.getFlowTimeBegin() == null || vo.getFlowTimeEnd() == null) {
            DateTime now = new DateTime();
            query.ge("flow_time", DateUtil.beginOfMonth(now));
            query.lt("flow_time", DateUtil.endOfMonth(now));
        }
        query.select("payment_id", "transaction_id", "pay_flow_id", "payment_channel", "order_amount", "amount", "refund_status", "create_time", "flow_time");
        List<PaymentRefundFlow> flows = paymentRefundFlowMapper.selectList(query);
        if (flows.isEmpty()) return Result.success("当前查询条件暂无数据");

        // 定义表头
        List<List<String>> head = new ArrayList<>();
        head.add(Collections.singletonList("序号"));
        head.add(Collections.singletonList("退款订单号"));
        head.add(Collections.singletonList("渠道订单号"));
        head.add(Collections.singletonList("支付订单号"));
        head.add(Collections.singletonList("支付渠道"));
        head.add(Collections.singletonList("订单金额(元)"));
        head.add(Collections.singletonList("退款金额(元)"));
        head.add(Collections.singletonList("退款状态"));
        head.add(Collections.singletonList("创建时间"));
        head.add(Collections.singletonList("交易时间"));

        // 定义数据
        List<List<Object>> data = new ArrayList<>();
        flows.forEach(flow -> {
            LocalDateTime createTime = flow.getCreateTime();
            LocalDateTime flowTime = flow.getFlowTime();
            data.add(Arrays.asList(
                    flows.indexOf(flow) + 1,
                    flow.getPaymentId(),
                    flow.getTransactionId(),
                    flow.getPayFlowId(),
                    PaymentChannelEnum.getDesc(flow.getPaymentChannel()),
                    NumberUtil.div(Optional.ofNullable(flow.getOrderAmount()).orElse(0), Integer.valueOf(100)),
                    NumberUtil.div(Optional.ofNullable(flow.getAmount()).orElse(0), Integer.valueOf(100)),
                    FlowStatusEnum.getDesc(flow.getRefundStatus()),
                    createTime != null ? createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "无",
                    flowTime != null ? flowTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "无"
            ));
        });

        ExcelBuildUtil.mapToExcel(head, data, response);
        return Result.success("导出成功");
    }

    @Override
    public List<RefundCouponSettleBO> queryCouponSettleRefund(LocalDateTime expectSettleTimeEnd) {
        return paymentRefundFlowMapper.queryCouponSettleRefund(expectSettleTimeEnd);
    }

    @Override
    public RefundResultBO appleRefund(AppleSignedTransactionInfo signedTransactionInfo, AppleNotificationTypeEnum refundType) {
        if (null == signedTransactionInfo) {
            return null;
        }
        String refundStatus = refundType.getCode().equals(AppleNotificationTypeEnum.REFUND.getCode()) ? FlowStatusEnum.SUCCESS.getCode() : FlowStatusEnum.FAIL.getCode();
        // 查询支付流水,没有的话就不做处理
        final PaymentOrder paymentOrder = paymentOrderService.querySuccessFlowByPaymentId(signedTransactionInfo.getOriginalTransactionId());
        PaymentRefundFlow refundFlow = null;

        final RefundResultBO refundResultBO = new RefundResultBO();
        refundResultBO.setPaymentChannel(PaymentChannelEnum.APPLE);
        refundResultBO.setRefundTime(LocalDateTime.now());
        refundResultBO.setStatus(refundStatus);
        refundResultBO.setTradeNo(signedTransactionInfo.getTransactionId());
        refundResultBO.setTransactionId(signedTransactionInfo.getTransactionId());
        if (null != paymentOrder && paymentOrder.getId() > 0) {
            // 有支付流水，处理退款
            refundResultBO.setAmount(paymentOrder.getOrderAmount());
            refundResultBO.setRefundAmount(paymentOrder.getOrderAmount());
            refundResultBO.setAmount(paymentOrder.getOrderAmount());
            refundResultBO.setPayFlowId(paymentOrder.getFlowId());

            QueryWrapper<PaymentRefundFlow> conditions = new QueryWrapper<>();
            conditions.eq("pay_flow_id", paymentOrder.getFlowId());
            // 根据支付单号查询退款记录，状态为：WAITING、SUCCESS的，查不到则新增退款单
            final PaymentRefundFlow waitRefundFlow = this.paymentRefundFlowMapper.selectOne(conditions);
            if (null == waitRefundFlow) {
                // 没有，新建退款记录
                final OrgPaymentChannelDetailBO paymentChannelDetailBO = orgPaymentChannelService.queryAllOrgPaymentChannelInfoById(paymentOrder.getOrgPaymentChannelId());
                String flowId = PaymentIdHelper.getRefund(paymentOrder.getUserId(), paymentOrder.getPaymentChannel(), null, paymentChannelDetailBO.getChannelOrderIdPre());
                refundResultBO.setRefundId(flowId);

                refundFlow = new PaymentRefundFlow();
                refundFlow.setFlowId(flowId);
                refundFlow.setPaymentId(signedTransactionInfo.getTransactionId());
                refundFlow.setPayFlowId(paymentOrder.getFlowId());
                refundFlow.setTradeNo(signedTransactionInfo.getTransactionId());
                refundFlow.setTransactionId(signedTransactionInfo.getTransactionId());
                refundFlow.setUserId(paymentOrder.getUserId());
                refundFlow.setUserTel(paymentOrder.getUserTel());
                // 实际退款金额
                refundFlow.setAmount(paymentOrder.getAmount());
                refundFlow.setRefundType(ChannelRefundTypeEnum.ALL.getCode());
                refundFlow.setRefundPlatformDiscount(0);
                refundFlow.setRefundChannelDiscount(0);
                refundFlow.setFlowTime(LocalDateTime.now());
                // 申请退款金额
                refundFlow.setRefundAmount(paymentOrder.getAmount());
                refundFlow.setOrderAmount(paymentOrder.getOrderAmount());
                refundFlow.setRefundStatus(refundStatus);
                refundFlow.setOrderName(paymentOrder.getOrderName());
                refundFlow.setOrderApp(paymentOrder.getOrderApp());
                refundFlow.setPaymentChannel(paymentOrder.getPaymentChannel());
                refundFlow.setPaymentWay(paymentOrder.getPaymentWay());
                refundFlow.setCreateTime(LocalDateTime.now());
                refundFlow.setPayScene(paymentOrder.getPayScene());
                refundFlow.setSlottingRate(paymentOrder.getSlottingRate());

                paymentRefundFlowMapper.insert(refundFlow);
                AtomicInteger subFlowInc = new AtomicInteger(1); // 子订单流水起始值
                final List<PaymentFlowDetail> flowDetails = paymentFlowDetailService.findByFlowId(paymentOrder.getFlowId());
                List<PaymentRefundFlowDetail> refundFlowDetails = flowDetails.stream().map(d -> {
                    PaymentRefundFlowDetail detail = new PaymentRefundFlowDetail();
                    detail.setFlowId(flowId);
                    detail.setPaymentId(signedTransactionInfo.getTransactionId());
                    detail.setAmount(d.getOrderAmount());
                    detail.setOrderAmount(d.getOrderAmount());
                    detail.setSubOrderId(d.getSubOrderId());
                    detail.setDiscount(d.getDiscount());
                    detail.setRefundStatus(refundStatus);
                    detail.setSubFlowId(PaymentIdHelper.getSubOrderFlowId(flowId, subFlowInc.get()));
                    detail.setRefundPlatformDiscount(d.getPlatformDiscount());
                    detail.setRefundAmount(d.getOrderAmount());
                    detail.setCouponSettleStatus("");

                    subFlowInc.getAndIncrement();
                    return detail;
                }).collect(Collectors.toList());
                refundFlowDetailService.saveBatch(refundFlowDetails);
                // TODO 推送订单中心生成退款单
            } else if (FlowStatusEnum.WAITING.getCode().equals(waitRefundFlow.getRefundStatus()) || !waitRefundFlow.getRefundStatus().equals(refundStatus)) {
                // 有，且状态为：WAITING，更新退款单状态
                refundResultBO.setRefundId(waitRefundFlow.getFlowId());
                this.refundResult(refundResultBO);
            } else {
                log.warn("苹果退款消息重复回调，不处理！");
                return null;
            }
            return refundResultBO;
        }
        log.error("苹果退款消息通知，未查询到支付订单或苹果退款消息重复回调");
        return null;
    }

    @Override
    public Result<GetRefundOrderResBO> queryRefundStatus(String refundFlowId) {
        PaymentRefundFlow refundFlow = getByFlowId(refundFlowId);
        Asserts.isNotNull(refundFlow, "未查询到退款流水！");
        GetRefundOrderResBO getRefundOrderResBO = new GetRefundOrderResBO();
        getRefundOrderResBO.setRefundId(refundFlow.getFlowId());
        getRefundOrderResBO.setRefundTime(refundFlow.getFlowTime());
        getRefundOrderResBO.setAmount(refundFlow.getAmount());
        getRefundOrderResBO.setChannelOrderStatusEnum(ChannelOrderStatusEnum.getByCode(refundFlow.getRefundStatus()));
        getRefundOrderResBO.setTransactionId(refundFlow.getTransactionId());
        getRefundOrderResBO.setTradeNo(refundFlow.getTradeNo());
        ChannelOrderStatusEnum orderStatusEnum = ChannelOrderStatusEnum.getByCode(refundFlow.getRefundStatus());
        getRefundOrderResBO.setChannelOrderStatusEnum(orderStatusEnum);
        PaymentOrder paymentOrder = paymentOrderService.get(refundFlow.getPayFlowId());

        getRefundOrderResBO.setPaymentChannel(paymentOrder.getPaymentChannel());
        getRefundOrderResBO.setPayFlowId(paymentOrder.getFlowId());
        if (refundFlow.getRefundStatus().equals(FlowStatusEnum.SUCCESS.getCode()) || refundFlow.getRefundStatus().equals(FlowStatusEnum.FAIL.getCode())) {
            getRefundOrderResBO.setChannelOrderStatusEnum(ChannelOrderStatusEnum.getByCode(refundFlow.getRefundStatus()));
            return Result.success(getRefundOrderResBO);
        }
        Asserts.isNotNull(paymentOrder, "未查询到支付流水！");
        AbstractPayChannel payChannel = PayChannelFactory.get(refundFlow.getPaymentChannel());
        GetRefundOrderBO getRefundOrderBO = new GetRefundOrderBO();
        getRefundOrderBO.setUid(refundFlow.getUserId());
        getRefundOrderBO.setRefundId(refundFlow.getFlowId());
        getRefundOrderBO.setOrgPaymentChannelId(paymentOrder.getOrgPaymentChannelId());
        Result<GetRefundOrderResBO> channelRefundResult = payChannel.getRefund(getRefundOrderBO);
        Asserts.isTrue(Objects.nonNull(channelRefundResult) && channelRefundResult.getRet() == 0, "查询渠道退款结果失败，原因" + channelRefundResult.getMsg());
        if (channelRefundResult.getData().getChannelOrderStatusEnum().equals(ChannelOrderStatusEnum.success)
                || channelRefundResult.getData().getChannelOrderStatusEnum().equals(ChannelOrderStatusEnum.fail)) {
            // 退款成功、退款失败，更新数据库状态
            PaymentRefundFlow updRefundFlow = new PaymentRefundFlow();
            updRefundFlow.setId(refundFlow.getId());
            updRefundFlow.setRefundStatus(channelRefundResult.getData().getChannelOrderStatusEnum().name());
            if (null != channelRefundResult.getData()) {
                updRefundFlow.setFlowTime(channelRefundResult.getData().getRefundTime());
                updRefundFlow.setAmount(channelRefundResult.getData().getAmount());
                updRefundFlow.setTransactionId(channelRefundResult.getData().getTransactionId());
                updRefundFlow.setTradeNo(channelRefundResult.getData().getTradeNo());

                getRefundOrderResBO.setRefundTime(updRefundFlow.getFlowTime());
                getRefundOrderResBO.setAmount(updRefundFlow.getAmount());
                getRefundOrderResBO.setTransactionId(updRefundFlow.getTransactionId());
                getRefundOrderResBO.setTradeNo(updRefundFlow.getTradeNo());
                getRefundOrderResBO.setChannelOrderStatusEnum(channelRefundResult.getData().getChannelOrderStatusEnum());

//                this.updateById(updRefundFlow);
//                // 修改退款明细退款状态
//                refundFlowDetailService.updateRefundStatus(refundFlow.getFlowId(), null, ChannelRefundTypeEnum.ALL,
//                        "success".equals(channelRefundResult.getData().getChannelOrderStatusEnum().name()) ? FlowStatusEnum.SUCCESS : FlowStatusEnum.FAIL);
            }
        }
        return Result.success(getRefundOrderResBO);
    }

    @Override
    public void verification(RefundVerificationRequestVO params) {
        PaymentRefundFlow refundFlow = getByFlowId(params.getFlowId());
        Asserts.isTrue(Objects.nonNull(refundFlow), "未查询到退款流水！");
        List<RefundSettle> refundSettles = refundSettleService.list(new LambdaQueryWrapper<RefundSettle>().eq(RefundSettle::getFlowId, refundFlow.getFlowId())
                .in(RefundSettle::getVerificationStatus, new ArrayList<>() {{
                    add(VerificationStatusEnum.WAIT.getCode());
                    add(VerificationStatusEnum.FL.getCode());
                }}));
        Asserts.isTrue(CollectionUtils.isNotEmpty(refundSettles), "未查询到退款结算单！");
        //List<RefundSettleDetail> refundSettleDetails = refundSettleDetailService.list(new LambdaQueryWrapper<RefundSettleDetail>().in(RefundSettleDetail::getSettleId, refundSettles.stream().map(RefundSettle::getId).toList()));
        List<RefundSettle> waitVerifications = refundSettles.stream().filter(rfd -> StringUtils.isEmpty(rfd.getVerificationStatus()) || rfd.getVerificationStatus().equals(VerificationStatusEnum.WAIT.getCode())).toList();
        if (CollectionUtils.isEmpty(waitVerifications)) {
            return;
        }

        RefundSettle refundSettle = new RefundSettle();
        refundSettle.setVerificationId(params.getVerificationId());
        refundSettle.setVerificationStatus(VerificationStatusEnum.SC.getCode());
        refundSettle.setSettleStatus(SettleStatusEnum.WAIT.getCode());
        refundSettle.setVerificationTime(LocalDateTime.now());
        // 应清日期,隔天清
        refundSettle.setSettleDate(LocalDate.now().plusDays(1));
        refundSettle.setUpdateTime(LocalDateTime.now());
        refundSettleService.update(refundSettle, new LambdaQueryWrapper<RefundSettle>().in(RefundSettle::getId, waitVerifications.stream().map(RefundSettle::getId).toList()));
    }

    private PaymentRefundFlow getByFlowId(String refundFlowId) {
        QueryWrapper<PaymentRefundFlow> conditions = new QueryWrapper<>();
        conditions.eq("flow_id", refundFlowId);
        return paymentRefundFlowMapper.selectOne(conditions);
    }
}
